package allod.camera

import com.xuggle.xuggler._
import actors._
import actors.Actor._
import message._
import java.awt.image.BufferedImage
import java.awt.Image

/**
 * Created by IntelliJ IDEA.
 * User: allod
 * Date: 4 трав 2010
 * Time: 23:13:06
 */

object CaptureService {
  private val (driverName, deviceName) = System.getProperty("os.name") match {
    case "Linux" => ("video4linux2", "/dev/video1")
    case os =>
      if (os.startsWith("Windows")) {
        ("vfwcap", "0")
      } else {
        throw new RuntimeException("Unsupported OS: " + os)
      }
  }
}

class CaptureService(videoProcessor: Handler, width: Int, height: Int) extends Actor {
  import CaptureService._
  if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION))
    throw new RuntimeException("could not find Xuggler (with IVideoResampler support)")

  private val packet: IPacket = IPacket.make

  private var container: IContainer = null

  private var videoCoder: IStreamCoder = null

  private val format = IContainerFormat.make
  if (format.setInputFormat(driverName) < 0)
    throw new IllegalArgumentException("couldn't open webcam device: " + driverName)

  private var resampler: IVideoResampler = null

  val bitRate = 2

  var framesToCapture = 0.0

  def act() {
    loop {
      react {
        case Snapshot =>
          if (framesToCapture > 0) {
            videoProcessor ! captureImage()
            framesToCapture -= 1
            self ! Snapshot
          } else {
            self ! Stop
          }
        case Start(framesCount) =>
          require(framesCount > 0)

          framesToCapture = framesCount
          if (container == null) {
            launchCamera()
          }
          self ! Snapshot
        case Stop =>
          disableCamera()
          framesToCapture = 0
      }
    }
  }

  def launchCamera() {
    container = createContainer
    var retval: Int = container.open(deviceName, IContainer.Type.READ, format)
    if (retval < 0) {
      var error: IError = IError.make(retval)
      throw new IllegalArgumentException("could not open file: " + deviceName + "; Error: " + error.getDescription)
    }
    videoCoder = createVideoCoder(container)
    if (videoCoder.open < 0)
      throw new RuntimeException("could not open video decoder for container: " + deviceName)
    resampler = createResampler(videoCoder)
  }

  def disableCamera() {
    if (videoCoder != null) {
      videoCoder.close
      videoCoder = null
    }
    if (container != null) {
      container.close
      container = null
    }
  }

  private def createContainer(): IContainer = {
    val cont = IContainer.make
    val params: IContainerParameters = IContainerParameters.make
    params.setTimeBase(IRational.make(30, 1))
    params.setVideoWidth(width)
    params.setVideoHeight(height)
    cont.setParameters(params)
    cont
  }

  private def createVideoCoder(cont: IContainer): IStreamCoder = {
    var numStreams: Int = cont.getNumStreams
    for (i <- 0 until numStreams) {
      val stream: IStream = cont.getStream(i)
      val coder: IStreamCoder = stream.getStreamCoder
      if (coder.getCodecType == ICodec.Type.CODEC_TYPE_VIDEO) {
        return coder
      }
    }
    throw new RuntimeException("could not find video stream")
  }

  private def createResampler(vCoder: IStreamCoder): IVideoResampler = {
    var videoResampler: IVideoResampler = null
    if (vCoder.getPixelType != IPixelFormat.Type.BGR24) {
      videoResampler = IVideoResampler.make(vCoder.getWidth, vCoder.getHeight,
        IPixelFormat.Type.BGR24, vCoder.getWidth, vCoder.getHeight, vCoder.getPixelType)
      if (videoResampler == null)
        throw new RuntimeException("could not create color space resampler for: " + deviceName)
    }
    return videoResampler
  }

  private def captureImage(): Image = {
    //    val time = System.currentTimeMillis

    var result: Image = null
    for (i <- 0 until bitRate) {
      if (container.readNextPacket(packet) >= 0 && i % bitRate == 0) {
        result = getImageFromPacket(packet)
      }
    }

    //    println("capture time " + (System.currentTimeMillis - time))
    result
  }

  private def getImageFromPacket(packet: IPacket): Image = {
    if (packet.getStreamIndex == videoCoder.getStream.getIndex) {
      var picture: IVideoPicture = IVideoPicture.make(videoCoder.getPixelType,
        videoCoder.getWidth, videoCoder.getHeight)
      var offset: Int = 0
      while (offset < packet.getSize) {
        var bytesDecoded: Int = videoCoder.decodeVideo(picture, packet, offset)
        if (bytesDecoded < 0) throw new RuntimeException("got error decoding video in: " + deviceName)
        offset += bytesDecoded
        if (picture.isComplete) {
          return convertToJavaImage(picture)
        }
      }
    }
    throw new IllegalStateException
  }

  private def convertToJavaImage(pic: IVideoPicture): BufferedImage = {
    val picture = if (resampler != null) resample(pic) else pic
    if (picture.getPixelType != IPixelFormat.Type.BGR24)
      throw new RuntimeException("could not decode video as BGR 24 bit data in: " + deviceName)
    val result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
    result.getGraphics.drawImage(Utils.videoPictureToImage(picture), 0, 0, null)
    return result
  }

  private def resample(picture: IVideoPicture): IVideoPicture = {
    var newPic: IVideoPicture = IVideoPicture.make(resampler.getOutputPixelFormat,
      picture.getWidth, picture.getHeight)
    if (resampler.resample(newPic, picture) < 0)
      throw new RuntimeException("could not resample video from: " + deviceName)
    return newPic
  }
}